﻿using System;
using System.Windows;
using System.Collections.Generic;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using Bling.DSL;
using Bling.WPF;
using Bling.WPF.Custom;
using Bling.WPF.Util;
using Bling.Core;
using Bling.Util;
using Bling.Properties;
using Bling.Mixins;
using Bling.Blocks;
using Bling.Shapes;
using Bling.Graphics;
using Bling.Matrices;
using Bling.Angles;
using Bling.Reactive;

namespace Bling.Tutorial {
  public class CubeTest : Canvas {
    public CubeTest() {
      var Canvas = this.Bl();
      Canvas.Background = Brushes.White;
      // completely composed transform for use in 2D simulation.
      Matrix4Bl MainTransform;
      Matrix4Bl WorldTransform, NoRotateTransform;
      QuaternionBl UseRotate = this.Property(QuaternionBl.Identity);

      // topology for the cube we want to render.
      var Cube = new CubeMesh();
      EventStream SpinLeft, SpinUp;

      {
        var SliderZ = new LabelSliderBl(Canvas) { Bottom = Canvas.Height, Left = 0, Minimum = 0, Maximum = 1, LabelName = "Z", Value = .5, ZIndex = 4 };
        var SpinLeftButton = new ButtonBl(Canvas) { LeftBottom = SliderZ.LeftTop, ZIndex = 4, Content = "Left" };
        var SpinUpButton = new ButtonBl(Canvas) { LeftBottom = SpinLeftButton.RightBottom, ZIndex = 4, Content = "Up" };
        SpinLeft = SpinLeftButton.Click1;
        SpinUp = SpinUpButton.Click1;

        Double3Bl WorldCenter = new Double3Bl(0, 0, 0);
        // camera position is used in lighting, so externalize
        Double3Bl CameraPosition = WorldCenter + new Double3Bl(0, 0, -4);
        PerspectiveCameraCl Camera = new PerspectiveCameraCl() {
          Position = CameraPosition,
        };

        WorldTransform = this.Property(Matrix4Bl.MakeScale(SliderZ.Value.XXX()) * (UseRotate).Matrix);
        MainTransform = this.Property(Camera.Matrix * WorldTransform);

        NoRotateTransform = this.Property(Matrix4Bl.MakeScale(SliderZ.Value.XXX()));
        NoRotateTransform = this.Property(Camera.Matrix * NoRotateTransform);
        
      }
      {
        //var animate = this.Property(0d.Bl());
        //UseRotate[Spin].Bind = new QuaternionBl(new Double3Bl(0, 1, 0), (animate.Ease.Quad.In * .5).PI()) * UseRotate[Spin];
        // animate[Spin] = 0d;
        //animate[Spin].Animate().To = 1d;

        UseRotate[SpinLeft].Animate().Tween(clock => clock.Ease.Quad.In).To =
          new QuaternionBl(new Double3Bl(0, 1, 0), (.5).PI()) * UseRotate[SpinLeft];

        UseRotate[SpinUp].Animate().Tween(clock => clock.Ease.Quad.In).To =
          new QuaternionBl(new Double3Bl(1, 0, 0), (.5).PI()) * UseRotate[SpinUp];

      }


      //var Points = new Double2Bl[2, 2, 2];

      var MinMax = new Double2Bl(1, 3);
      Func<int, Double3Bl> PointFor = (i) => {
        var xyz = Cube[i];
        var q00 = MainTransform.Transform(xyz.Position);
        var q0 = (q00 + 1d) / 2d;
        var q = new Double2Bl(q0.X, 1d - q0.Y);
        q = q * Canvas.Size;
        return new Double3Bl(q, q0.Z);
      };

      Double2Bl leftTop, rightBot;
      { // look at first face.
        //Double2Bl max = double.MinValue, min = double.MaxValue;
        Func<int, Double3Bl> PointFor2 = (i) => {
          var xyz = Cube[i];
          var q00 = NoRotateTransform.Transform(xyz.Position);
          var q0 = (q00 + 1d) / 2d;
          var q = new Double2Bl(q0.X, 1d - q0.Y);
          q = q * Canvas.Size;
          return new Double3Bl(q, q0.Z);
        };
        leftTop = this.Property(PointFor2(1).XY());
        rightBot = this.Property(PointFor2(3).XY());
      }

      //Func<IntBl, Double2Bl> PointFor0 = j => ellipses[j.CurrentValue].CenterPosition;
      Func<int, Double2Bl> PointFor0 = j => PointFor(j).XY();
      StringBl str = "";
      for (int i = 0; i < 6; i++) {
        var face = Cube.GetFace(i);
        var startVertex = 4 * i;
        var normal = this.Property(UseRotate.Transform(face[0].Normal));
        var z = normal.Z;
        //z = this.Property(-z.RoundN(1000));
        z = this.Property(-z.RoundN(1000));
        //var Accum = z (z > 0 & z < 1).Condition(z, 0);

        var light = normal.Dot(new Double3Bl(0, 0, -1));
        var shape = new PathShapeBl(Canvas) {
          Start = PointFor0(startVertex + 0),
          Stroke = { Thickness = 0 },
          Fill = Brushes.Black,
          //Fill = HolderBrush, // ,
          ZIndex = (IntBl)((20d * z).Round * 2d) + 0,
          IsHitTestVisible = false,
          //Opacity = light,
          Visibility = (z > 0),
          //Opacity = (z > 0).Condition(0.5, 0),
        };
        str = str + (z > 0).Condition("i: ".Bl() + i.Bl() + " " + z, "");

        /*
        var shapeB = new PathShapeBl(Canvas) {
          Start = shape.Start,
          Stroke = { Thickness = 0 },
          Fill = Brushes.Black,
          //Fill = HolderBrush, // ,
          ZIndex = (IntBl)((20d * z).Round * 2d) + 1,
          IsHitTestVisible = false,
          //Opacity = light,
          Visibility = shape.Visibility,
          Opacity = 1d - light,
          //Opacity = (z > 0).Condition(0.5, 0),
        };
         */


        CanvasBl Holder = new CanvasBl(Canvas) {
         LeftTop = leftTop.XY(),
         Size = (rightBot - leftTop).XY(),
         Background = Brushes.White,
         ZIndex = this.Property(z == 1).Condition(42.Bl(), 0),
         ClipToBounds = true,
        };
        new TextBoxBl(Holder) {
          CenterPosition = Holder.CenterSize,
          Text = "Hello world " + i + "\n" +
          "Hello world " + i + "\n"+
          "Hello world " + i + "\n"+
          "Hello world " + i + "\n",
          Font = { Weight = FontWeights.SemiBold, Size = 100 },
        };
        var HolderBrush = new VisualBrushBl() { Visual = Holder, TileMode = TileMode.None, Stretch = Stretch.Fill };

        shape.Effect.Custom = (input, uv) => {
          return new ColorBl(light.Lerp(Colors.Black.Bl(), HolderBrush[uv]).ScRGB, input[uv].ScA);
        };


        for (int j = 0; j < 4; j++) {
          var seg = new LineSegmentBl() {
            Point = PointFor0(startVertex + ((j + 1) % 4)),
          };
          shape.Segments.AddOne = seg;
          //shapeB.Segments.AddOne = seg;
        }
      }


      new LabelBl(Canvas) {
        ZIndex = 1000, Content = str, RightTop = new Double2Bl(Canvas.Width, 0),
      };

      // virtual sphere sphere
      var sz = Canvas.Size - 20d;
      {
        var SphereDiameter = sz.X.Min(sz.Y);
        var sphere = new EllipseBl(Canvas) {
          Size = SphereDiameter,
          CenterPosition = Canvas.CenterSize,
          ZIndex = 2,
          Fill = Brushes.LightGray,
          Opacity = .1,
          IsHitTestVisible = false,
        };
        var outer = new EllipseBl(Canvas) {
          Size = SphereDiameter + 10d,
          CenterPosition = Canvas.CenterSize,
          ZIndex = 1,
          Fill = Brushes.Black,
          Opacity = .1,
        };
        {
          var MouseAt = sphere.Mouse.Position / sphere.Size;
          MouseAt = (MouseAt * 2d - 1d);
          MouseAt = MouseAt * new Double2Bl(-1, +1);
          var MouseAt3D = new Double3Bl(MouseAt, (1d - MouseAt.LengthSquared).Max(0d).Sqrt).RawNormalize;

          UseRotate[outer.Mouse.Drag.In].Stop(true);
          UseRotate[outer.Mouse.Drag.Update] =
            MouseAt3D.AngleBetween(MouseAt3D[outer.Mouse.Drag.In]) * UseRotate[outer.Mouse.Drag.In];
        }
      }
    }
  }
}